package de.stefanocke.roo.xtend;

import static de.stefanocke.roo.xtend.Annotation.annotationsToBuilders;

import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

import org.springframework.roo.classpath.details.InvocableMemberMetadata;
import org.springframework.roo.classpath.details.MethodMetadata;
import org.springframework.roo.classpath.details.annotations.AnnotatedJavaType;
import org.springframework.roo.classpath.details.annotations.AnnotationMetadataBuilder;
import org.springframework.roo.model.JavaSymbolName;
import org.springframework.roo.model.JavaType;

public class MethodSignature {
	private List<AnnotationMetadataBuilder> annotations = new ArrayList<AnnotationMetadataBuilder>();
	private int modifier = Modifier.PUBLIC;
	private JavaType returnType = JavaType.VOID_PRIMITIVE;
	private JavaSymbolName name;
	private List<Parameter> parameters = new ArrayList<Parameter>();
	private List<JavaType> exceptions = new ArrayList<JavaType>();
	private boolean constructor = false;

	public MethodSignature(List<AnnotationMetadataBuilder> annotations, int modifier, JavaType returnType,
			JavaSymbolName name, List<Parameter> parameters, List<JavaType> exceptions) {
		super();
		if (annotations != null) {
			this.annotations = annotations;
		}
		if (modifier != 0) {
			this.modifier = modifier;
		}
		if (returnType != null) {
			this.returnType = returnType;
		}
		this.name = name;
		if (parameters != null) {
			this.parameters = parameters;
		}
		if (exceptions != null) {
			this.exceptions = exceptions;
		}
	}

	/**
	 * Creates a constructor signature
	 */
	public MethodSignature(List<AnnotationMetadataBuilder> annotations, int modifier,

	List<Parameter> parameters, List<JavaType> exceptions) {
		super();
		constructor = true;
		if (annotations != null) {
			this.annotations = annotations;
		}
		if (modifier != 0) {
			this.modifier = modifier;
		}

		if (parameters != null) {
			this.parameters = parameters;
		}
		if (exceptions != null) {
			this.exceptions = exceptions;
		}
	}

	public boolean isConstructor() {
		return constructor;
	}

	public List<AnnotationMetadataBuilder> getAnnotations() {
		return annotations;
	}

	public void setAnnotations(List<AnnotationMetadataBuilder> annotations) {
		this.annotations = annotations;
	}

	public int getModifier() {
		return modifier;
	}

	public void setModifier(int modifier) {
		this.modifier = modifier;
	}

	public JavaType getReturnType() {
		return returnType;
	}

	public void setReturnType(JavaType returnType) {
		this.returnType = returnType;
	}

	public JavaSymbolName getName() {
		return name;
	}

	public void setName(JavaSymbolName name) {
		this.name = name;
	}

	public List<Parameter> getParameters() {
		return parameters;
	}

	public void setParameters(List<Parameter> parameters) {
		this.parameters = parameters;
	}

	public List<JavaType> getExceptions() {
		return exceptions;
	}

	public void setExceptions(List<JavaType> exceptions) {
		this.exceptions = exceptions;
	}

	public MethodSignature withThrows(JavaType... exceptions) {
		this.exceptions.addAll(Arrays.asList(exceptions));
		return this;
	}

	public MethodSignature withThrows(String... exceptionFqns) {
		for (String fqn : exceptionFqns) {
			this.exceptions.add(new JavaType(fqn));
		}
		return this;
	}

	public MethodSignature withAnnotations(AnnotationMetadataBuilder... annotations) {
		this.annotations.addAll(Arrays.asList(annotations));
		return this;
	}

	public static MethodSignature signature(int modifier, JavaType returnType, String name, Parameter... parameters) {
		return new MethodSignature(null, modifier, returnType, new JavaSymbolName(name), Arrays.asList(parameters),
				null);
	}
	
	public static MethodSignature signature(JavaType returnType, String name, Parameter... parameters) {
		return new MethodSignature(null, Modifier.PUBLIC, returnType, new JavaSymbolName(name), Arrays.asList(parameters),
				null);

	}

	public static MethodSignature signature(int modifier, JavaType returnType, String name, List<Parameter> parameters) {
		return new MethodSignature(null, modifier, returnType, new JavaSymbolName(name), parameters, null);

	}

	public static MethodSignature signature(JavaType returnType, String name, List<Parameter> parameters) {
		return new MethodSignature(null, Modifier.PUBLIC, returnType, new JavaSymbolName(name), parameters, null);

	}
	/**
	 * Creates a constructor signature
	 * 
	 * @param modifier
	 * @param parameters
	 * @return
	 */
	public static MethodSignature signature(int modifier, Parameter... parameters) {
		return new MethodSignature(null, modifier, Arrays.asList(parameters), null);

	}

	/**
	 * Creates a constructor signature
	 * 
	 * @param modifier
	 * @param parameters
	 * @return
	 */
	public static MethodSignature signature(int modifier, List<Parameter> parameters) {
		return new MethodSignature(null, modifier, parameters, null);

	}

	public static MethodSignature signature(Parameter... parameters) {
		return signature(Modifier.PUBLIC, parameters);

	}
	
	public static MethodSignature signature(List<Parameter> parameters) {
		return new MethodSignature(null, Modifier.PUBLIC, parameters, null);

	}
	

	public static MethodSignature signature(int modifier, String returnTypeFqn, String name, Parameter... parameters) {
		return new MethodSignature(null, modifier, new JavaType(returnTypeFqn), new JavaSymbolName(name),
				Arrays.asList(parameters), null);

	}

	public static MethodSignature signature(List<AnnotationMetadataBuilder> annotations, int modifier,
			JavaType returnType, String name, Parameter... parameters) {
		return new MethodSignature(annotations, modifier, returnType, new JavaSymbolName(name),
				Arrays.asList(parameters), null);

	}

	/**
	 * Creates a constructor signature
	 * 
	 * @param annotations
	 * @param modifier
	 * @param parameters
	 * @return
	 */
	public static MethodSignature signature(List<AnnotationMetadataBuilder> annotations, int modifier,
			Parameter... parameters) {
		return new MethodSignature(annotations, modifier, Arrays.asList(parameters), null);

	}

	public static MethodSignature signature(List<AnnotationMetadataBuilder> annotations, int modifier,
			String returnTypeFqn, String name, Parameter... parameters) {
		return new MethodSignature(annotations, modifier, new JavaType(returnTypeFqn), new JavaSymbolName(name),
				Arrays.asList(parameters), null);

	}

	public static MethodSignature signature(MethodMetadata mm) {
		List<Parameter> parameters = parameters(mm);
		return new MethodSignature(annotationsToBuilders(mm.getAnnotations()), mm.getModifier(), mm.getReturnType(),
				mm.getMethodName(), parameters, mm.getThrowsTypes());
	}

	public static List<Parameter> parameters(InvocableMemberMetadata mm) {
		List<Parameter> parameters = new ArrayList<Parameter>();
		List<AnnotatedJavaType> paramTypes = mm.getParameterTypes();
		List<JavaSymbolName> paramNames = mm.getParameterNames();

		for (int i = 0; i < paramTypes.size(); i++) {
			parameters.add(new Parameter(annotationsToBuilders(paramTypes.get(i).getAnnotations()), paramTypes.get(i)
					.getJavaType(), paramNames.get(i)));

		}
		return parameters;
	}

	public static MethodSignature signature(AnnotationMetadataBuilder annotation, int modifier, JavaType returnType,
			String name, Parameter... parameters) {
		return new MethodSignature(Collections.singletonList(annotation), modifier, returnType,
				new JavaSymbolName(name), Arrays.asList(parameters), null);

	}

	public static MethodSignature signature(AnnotationMetadataBuilder annotation, int modifier, String returnTypeFqn,
			String name, Parameter... parameters) {
		return new MethodSignature(Collections.singletonList(annotation), modifier, new JavaType(returnTypeFqn),
				new JavaSymbolName(name), Arrays.asList(parameters), null);

	}

}
